home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / Other Langs / Tickle-4.0 (tcl) / library / help.tcl < prev    next >
Encoding:
Text File  |  1993-11-06  |  10.3 KB  |  352 lines  |  [TEXT/MPS ]

  1. #
  2. # help.tcl --
  3. #
  4. # Tcl help command. (see TclX manual)
  5. #------------------------------------------------------------------------------
  6. # Copyright 1992-1993 Karl Lehenbauer and Mark Diekhans.
  7. #
  8. # Permission to use, copy, modify, and distribute this software and its
  9. # documentation for any purpose and without fee is hereby granted, provided
  10. # that the above copyright notice appear in all copies.  Karl Lehenbauer and
  11. # Mark Diekhans make no representations about the suitability of this
  12. # software for any purpose.  It is provided "as is" without express or
  13. # implied warranty.
  14. #------------------------------------------------------------------------------
  15. # The help facility is based on a hierarchical tree of subjects (directories)
  16. # and help pages (files).  There is a virtual root to this tree. The root
  17. # being the merger of all "help" directories found along the $auto_path
  18. # variable.
  19. #------------------------------------------------------------------------------
  20. # $Id: help.tcl,v 2.5 1993/06/24 07:30:29 markd Exp $
  21. #------------------------------------------------------------------------------
  22. #
  23.  
  24. #@package: TclX-help help helpcd helppwd apropos
  25.  
  26. #------------------------------------------------------------------------------
  27. # Return a list of help root directories.
  28.  
  29. proc help:RootDirs {} {
  30.     global auto_path
  31.     set roots {}
  32.     foreach dir $auto_path {
  33.         if [file isdirectory $dir:help] {
  34.             lappend roots $dir:help
  35.         }
  36.     }
  37.     return $roots
  38. }
  39.  
  40. #------------------------------------------------------------------------------
  41. # Take a path name which might have "." and ".." elements and flatten them out.
  42. # Also removes trailing and adjacent ":", unless its the only character.
  43.  
  44. proc help:FlattenPath pathName {
  45.     set newPath {}
  46.     foreach element [split $pathName :] {
  47.         if {[lempty $element]} continue
  48. ###     if {"$element" == "." || [lempty $element]} continue
  49.  
  50.         if {"$element" == ""} {
  51.             if {[llength [join $newPath :]] == 0} {
  52.                 error "Help: name goes above subject directory root"}
  53.             lvarpop newPath [expr [llength $newPath]-1]
  54.             continue
  55.         }
  56.         lappend newPath $element
  57.     }
  58.     set newPath [join $newPath :]
  59.  
  60.     # Take care of the case where we started with something line "/" or "/."
  61.  
  62.     if {("$newPath" == "") && [string match ":*" $pathName]} {
  63.         set newPath ":"
  64.     }
  65.         
  66.     return $newPath
  67. }
  68.  
  69. #------------------------------------------------------------------------------
  70. # Given a pathName relative to the virtual help root, convert it to a list of
  71. # real file paths.  A list is returned because the path could be "/", returning
  72. # a list of all roots. The list is returned in the same order of the auto_path
  73. # variable. If path does not start with a "/", it is take as relative to the
  74. # current help subject.  Note:  The root directory part of the name is not
  75. # flattened.  This lets other commands pick out the part relative to the
  76. # one of the root directories.
  77. proc help:ConvertPath pathName {
  78.     global TCLENV
  79.  
  80.     if {![string match ":*" $pathName]} {
  81.         if {"$TCLENV(help:curSubject)" == ":"} {
  82.             set pathName ":$pathName"
  83.         } else {
  84.             set pathName "$TCLENV(help:curSubject):$pathName"
  85.         }
  86.     }
  87.     set pathName [help:FlattenPath $pathName]
  88.  
  89.     # If the virtual root is specified, return a list of directories.
  90.  
  91.     if {$pathName == ":"} {
  92.         return [help:RootDirs]
  93.     }
  94.  
  95.     # Not the virtual root find the first match.
  96.  
  97.     foreach dir [help:RootDirs] {
  98.         if [file readable $dir:$pathName] {
  99.             return [list $dir:$pathName]
  100.         }
  101.     }
  102.     error "\"$pathName\" does not exist"
  103. }
  104.  
  105. #------------------------------------------------------------------------------
  106. # Return the virtual root relative name of the file given its absolute path.
  107. # The root part of the path should not have been flattened, as we would not
  108. # be able to match it.
  109.  
  110. proc help:RelativePath pathName {
  111.     foreach dir [help:RootDirs] {
  112.         if {[csubstr $pathName 0 [clength $dir]] == $dir} {
  113.             set name [csubstr $pathName [clength $dir] end]
  114.             if {$name == ""} {set name :}
  115.             return $name
  116.         }
  117.     }
  118.     if ![info exists found] {
  119.         error "problem translating \"$pathName\""
  120.     }
  121.  
  122. }
  123.  
  124. #------------------------------------------------------------------------------
  125. # Given a list of path names to subjects generated by ConvertPath, return
  126. # the contents of the subjects.  Two lists are returned, subjects under that
  127. # subject and a list of pages under the subject.  Both lists are returned
  128. # sorted.  This merges all the roots into a virtual root.  pathName is the
  129. # string that was passed to ConvertPath and is used for error reporting.
  130. # *.brk files are not returned.
  131.  
  132. proc help:ListSubject {pathName pathList subjectsVar pagesVar} {
  133.     upvar $subjectsVar subjects $pagesVar pages
  134.  
  135.     set subjects {}
  136.     set pages {}
  137.     set foundDir 0
  138.     foreach dir $pathList {
  139.         if ![file isdirectory $dir] continue
  140.         set foundDir 1
  141.         foreach file [glob -nocomplain $dir:*] {
  142.             if [string match *.brf $file] continue
  143.             if [file isdirectory $file] {
  144.                 lappend subjects [file tail $file]:
  145.             } else {
  146.                 lappend pages [file tail $file]
  147.             }
  148.         }
  149.     }
  150.     if !$foundDir {
  151.         error "\"$pathName\" is not a subject"
  152.     }
  153.     set subjects [lsort $subjects]
  154.     set pages [lsort $pages]
  155.     return {}
  156. }
  157.  
  158. #------------------------------------------------------------------------------
  159. # Display a line of output, pausing waiting for input before displaying if the
  160. # screen size has been reached.  Return 1 if output is to continue, return
  161. # 0 if no more should be outputed, indicated by input other than return.
  162. #
  163.  
  164. proc help:Display line {
  165.     global TCLENV
  166.     
  167.     if {([info globals MACINTOSH] == "") || \
  168.          ([info globals THINK_CONSOLE] != "")} {
  169.         if {$TCLENV(help:lineCnt) >= 23} {
  170.             set TCLENV(help:lineCnt) 0
  171.             puts stdout ":" nonewline
  172.             flush stdout
  173.             gets stdin response
  174.             if {![lempty $response]} {
  175.                 return 0
  176.             }
  177.         }
  178.     }
  179.     
  180.     puts stdout $line
  181.     incr TCLENV(help:lineCnt)
  182. }
  183.  
  184. #------------------------------------------------------------------------------
  185. # Display a help page (file).
  186.  
  187. ###
  188. ### TGE - completely re-written for the Mac.
  189. ###
  190. proc help:DisplayPage filePath {
  191.     if {[info globals TICKLE] != ""} {
  192.         open_file_window "$filePath" text
  193.     } else {
  194.         set inFH [open $filePath r]
  195.         set pagedata [read $inFH]
  196.         ###
  197.         ### This transliteration fixes the brain damaged ThinkC Console...
  198.         ###
  199.         if {[info globals THINK_CONSOLE] != ""} {
  200.             while { 1 } {
  201. if [regexp {^([^
  202. ]*)
  203. (.*)} $pagedata match sub1 pagedata] { if {![help:Display $sub1]} { break } } else {
  204.                 help:Display $pagedata
  205.                 break
  206.                 }
  207.             }
  208.         } else {
  209.             puts stdout $pagedata
  210.             close $inFH
  211.         }
  212.     }
  213. }    
  214.  
  215. #------------------------------------------------------------------------------
  216. # Display a list of file names in a column format. This use columns of 14 
  217. # characters 3 blanks.
  218.  
  219. proc help:DisplayColumns {nameList} {
  220.     set count 0
  221.     set outLine ""
  222.     foreach name $nameList {
  223.         if {$count == 0} {
  224.             append outLine "   "}
  225.         append outLine $name
  226.         if {[incr count] < 4} {
  227.             set padLen [expr 17-[clength $name]]
  228.             if {$padLen < 3} {
  229.                set padLen 3}
  230.             append outLine [replicate " " $padLen]
  231.         } else {
  232.            if {![help:Display $outLine]} {
  233.                return}
  234.            set outLine ""
  235.            set count 0
  236.         }
  237.     }
  238.     if {$count != 0} {
  239.         help:Display [string trimright $outLine]}
  240.     return
  241. }
  242.  
  243. #------------------------------------------------------------------------------
  244. # Display help on help, the first occurance of a help page called "help" in
  245. # the help root.
  246.  
  247. proc help:HelpOnHelp {} {
  248.     set helpPage [lindex [help:ConvertPath :help] 0]
  249.     if [lempty $helpPage] {
  250.         error "No help page on help found"
  251.     }
  252.     help:DisplayPage $helpPage
  253. }
  254.  
  255. #------------------------------------------------------------------------------
  256. # Help command.
  257.  
  258. proc help {{what {}}} {
  259.     global TCLENV
  260.  
  261.     set TCLENV(help:lineCnt) 0
  262.  
  263.     # Special case "help help", so we can get it at any level.
  264.  
  265.     if {($what == "help") || ($what == "?")} {
  266.         help:HelpOnHelp
  267.         return
  268.     }
  269.  
  270.     set pathList [help:ConvertPath $what]
  271.     if [file isfile [lindex $pathList 0]] {
  272.         help:DisplayPage [lindex $pathList 0]
  273.         return
  274.     }
  275.  
  276.     help:ListSubject $what $pathList subjects pages
  277.     set relativeDir [help:RelativePath [lindex $pathList 0]]
  278.  
  279.     if {[llength $subjects] != 0} {
  280.         help:Display "\nSubjects available in $relativeDir"
  281.         help:DisplayColumns $subjects
  282.     }
  283.  
  284.     if {[llength $pages] != 0} {
  285.         help:Display "\nHelp pages available in $relativeDir"
  286.         help:DisplayColumns $pages
  287.     }
  288. }
  289.  
  290.  
  291. #------------------------------------------------------------------------------
  292. # helpcd command.  The name of the new current directory is assembled from the
  293. # current directory and the argument.
  294.  
  295. proc helpcd {{dir :}} {
  296.     global TCLENV
  297.  
  298.     set pathName [lindex [help:ConvertPath $dir] 0]
  299.  
  300.     if {![file isdirectory $pathName]} {
  301.         error "Helpcd: \"$dir\" is not a subject"}
  302.  
  303.     set TCLENV(help:curSubject) [help:RelativePath $pathName]
  304.     return
  305. }
  306.  
  307. #------------------------------------------------------------------------------
  308. # Helpcd main.
  309.  
  310. proc helppwd {} {
  311.         global TCLENV
  312.         echo "Current help subject: $TCLENV(help:curSubject)"
  313. }
  314.  
  315. #------------------------------------------------------------------------------
  316. # apropos command.  This search the 
  317.  
  318. proc apropos {regexp} {
  319.     global TCLENV
  320.  
  321.     set TCLENV(help:lineCnt) 0
  322.  
  323.     set ch [scancontext create]
  324.     scanmatch -nocase $ch $regexp {
  325.         set path [lindex $matchInfo(line) 0]
  326.         set desc [lrange $matchInfo(line) 1 end]
  327.         if {![help:Display [format "%s - %s" $path $desc]]} {
  328.             set stop 1
  329.             return
  330.             }
  331.     }
  332.     set stop 0
  333.     foreach dir [help:RootDirs] {
  334.         foreach brief [glob -nocomplain $dir:*.brf] {
  335.             set briefFH [open $brief]
  336.             scanfile $ch $briefFH
  337.             close $briefFH
  338.             if $stop break
  339.         }
  340.         if $stop break
  341.     }
  342.     scancontext delete $ch
  343. }
  344.  
  345. #------------------------------------------------------------------------------
  346. # One time initialization done when the file is sourced.
  347. #
  348. global TCLENV
  349.  
  350. set TCLENV(help:curSubject) ":"
  351.